from pydocx import PyDocX
from docx import Document       # pip install python-docx
import re
import os
from docx.shared import Inches, Cm, Pt    #照片尺寸
import sys
import docx2pdf


def show_output(document, open_type='doc', output_doc='output_doc.docx', output_html='output_doc.html'):
    if sys.platform == 'win32':
        preffix = 'start '
    else:
        preffix = 'open '

    file_name = os.path.splitext(output_doc)[0]

    # --- 输出doc
    document.save(output_doc)

    # --------- 转化为html
    html = PyDocX.to_html(output_doc)
    f = open(output_html, 'w', encoding="utf-8")
    f.write(html)
    f.close()

    # --- convert to pdf
    # from bdtime import tt
    # tt.sleep(0.1)
    output_pdf = file_name + '.pdf'
    docx2pdf.convert(os.path.abspath(output_doc), os.path.abspath(output_pdf))

    if open_type == '__None__':
        print('--- 输出路径:', output_doc)
        return False
    elif open_type == '__all__':
        os.system(preffix + output_doc)
        os.system(preffix + output_html)
        os.system(preffix + output_pdf)
    elif open_type == 'doc':
        os.system(preffix + output_doc)
    elif open_type == 'html':
        os.system(preffix + output_html)
    elif open_type == 'pdf':
        os.system(preffix + output_pdf)
    else:
        raise ValueError('open_type取值错误!')
    return True


def find_match_by_sign_in_text(text, pattern=r'{{ ?(\w+) ?}}'):
    """
    找到text中的标识符sign, 如{{ name }}这类标签

    :param text: 待匹配文本
    :return: 正则匹配结果
    """
    reg = re.compile(pattern)
    match = reg.search(text)
    return match


def highlight_sections_docx(p, positions, text):
    """ Add characters to a text to highlight the segments indicated by
     a list of alternating start and end positions """
    # document = docx.Document()
    # p = document.add_paragraph()
    for i, (start, end) in enumerate(zip([None] + positions, positions + [None])):
        run = p.add_run(text[start:end])
        if i % 2:  # odd segments are highlighted
            run.bold = True   # or add other formatting - see https://python-docx.readthedocs.io/en/latest/api/text.html#run-objects
    return p


def find_context_by_match(match):
    """
    找到match对应的上下文context

    :param match: 正则匹配结果
    :return: 是否为图片is_picture, context_dc中的key和value
    """
    key = match.group(1)     # 变量名
    value = context_dc.get(key)

    is_picture = False
    if key.endswith('_picture') or key.endswith('Picture'):   # 判断是否为图片
        is_picture = True
    return is_picture, key, value


def _traverse1(table_i):
    print('~~~~~~~~~~~~~~~~ 开始遍历table_i')
    for row in table_i.rows:
        # print('row.cells --- ', len(row.cells))
        for cell in row.cells:
            for p in cell.paragraphs:
                print(p, p.text)

                text = p.text
                # reg = re.compile(r'{{ ?(\w+) ?}}')
                # match = reg.findall(text)

                match = find_match_by_sign_in_text(text)
                if match:
                    print('---- match', match)

                    is_picture, key, value = find_context_by_match(match)

                    if is_picture:
                        # 插入图片
                        assert value and os.path.isfile(value), '图片value错误!'
                        pic_format_ls = ['.jpg', '.jpeg', '.png']
                        assert os.path.splitext(value)[-1] in pic_format_ls, f'图片格式错误! 必须为{pic_format_ls}!'

                        run = p.add_run()
                        picture = run.add_picture(value)
                    else:
                        # 替换变量
                        new_text = reg.sub(value, text, 1)
                        p.text = new_text
        print('--- end traverse')
        return 1


def find_table_s_index(table):
    row_cells, column_cells = [], []
    index = []
    width, length = len(table.columns), len(table.rows)
    k = 0
    for row in table.rows:
        for cell in row.cells:
            if cell not in row_cells:
                index.append([k//width, k%width])
                row_cells.append(cell)
            k += 1
    k = 0
    for column in table.columns:
        for cell in column.cells:
            if cell not in column_cells:
                column_cells.append(cell)
            elif [k%length, k//length] in index:
                index.remove([k%length, k//length])
            k += 1
    return index


def show_table(table):
    index = find_table_s_index(table)
    for i in index:
        cell = table.rows[i[0]].cells[i[1]]
        print(cell.text)

def _traverse(table_i, pattern=r'{{ ?(\w+) ?}}'):
    print('~~~~~~~~~~~~~~~~ 开始遍历table_i')
    reg = re.compile(pattern)
    print(table_i)

    index = find_table_s_index(table_i)
    for i in index:
        cell = table_i.rows[i[0]].cells[i[1]]
        for p in cell.paragraphs:
            print(p, p.text)

            # <editor-fold desc="runs的预处理, 防止标签被拆开">
            def precessing_sign_in_p_runs(p):
                surrounding_len = 3  # 向前4个查找
                if 1:
                    pn = len(p.runs)
                    for ri in range(pn):
                        run = p.runs[ri]
                        text = run.text
                        print('---', text, '---')

                        def check_sign(text):
                            """
                            检查text是否只含有"}}"而没有"{{".
                            text可以为list类型
                            text = r_str_ls
                            """
                            if "}}" in text and "{{" not in text:
                                return True
                            return False
                        if check_sign(text):
                            print('run中断标识符的情况出现!')
                            # surrounding_runs = p.runs[max(ri - surrounding_len -1, 0): ri + 1]
                            # [r.text for r in surrounding_runs]

                            r_str_ls = [run.text]
                            i_break = -1
                            for _ri in range(max(ri - surrounding_len -1, 0), ri).__reversed__():
                                r = p.runs[_ri]
                                # print(_ri, r.text)
                                r_str_ls.append(r.text.strip())
                                if not check_sign(r_str_ls):
                                    i_break = _ri
                                    break

                            if i_break != -1:
                                r_str_ls.reverse()
                                run_text = ''.join(r_str_ls)
                                run.text = run_text

                                for _ri in range(i_break, ri):
                                    r = p.runs[_ri]
                                    r.text = ""
                    print(1)
                return 1
            # if 'signaturePicture' in p.text:
            #     print(p.text)
            #     for run in p.runs:  print('***', run.text)  # 这时候被拆开的标签应该被合并为一行了
                # precessing_sign_in_p_runs(p)

            precessing_sign_in_p_runs(p)
            # </editor-fold>

            for ri in range(len(p.runs)):
                run = p.runs[ri]

                text = run.text
                print(text)
                findall = reg.findall(text)
                if len(findall):
                    print(findall)
                    finditer = reg.finditer(text)
                    new_text = text
                    for match in finditer:
                        # <editor-fold desc="处理一个match, 获取new_text">
                        print('---- match', match)
                        is_picture, key, value = find_context_by_match(match)
                        if is_picture:
                            print('~~~~~~', is_picture, key, value)
                            # 插入图片
                            assert value and os.path.isfile(value), '图片value错误!'
                            pic_format_ls = ['.jpg', '.jpeg', '.png']
                            assert os.path.splitext(value)[-1] in pic_format_ls, f'图片格式错误! 必须为{pic_format_ls}!'

                            # run = p.add_run()
                            from docx.enum.table import WD_ALIGN_VERTICAL

                            picture = run.add_picture(value)
                            cell.vertical_alignment = WD_ALIGN_VERTICAL.TOP

                            # picture.height = Cm(6.71)   # 设置图片高度
                            # picture.width = Cm(8.49)    # 设置图片宽度
                        else:
                            # 替换变量
                            assert value, f'value为空! key: ' + key

                            print('~~~ 替换变量', key, value)
                            new_reg = re.compile(match.group())
                            new_text = new_reg.sub(str(value), new_text, 1)

                        # </editor-fold>

                    print('*** new_text: ', new_text)
                    run.text = new_text
                    # i
                    # table_i.rows[i[0]].cells[i[1]].text

    return table_i
# _traverse(table_i)


class OperateDoc:
    def __init__(self,
                 input_path='',       # doc读入路径
                 output_path='',       # doc输出路径
                 context_dc=None,       # 变量文本
                 pattern=r'{{ ?(\w+) ?}}',      # 变量匹配格式
                 open_type="doc",       # 打开方式
                 ):
        self.open_type = open_type
        self.output_path = output_path
        self.context_dc = context_dc
        self.pattern = pattern
        self.document = Document(input_path)
        self.table_ls = self.document.tables
        self.table_i = self.table_ls[0]     # 只有一个表格

    def traverse(self):
        table_i = _traverse(self.table_i, pattern=self.pattern)
        table_i
        show_table(table_i)
        return 1

    def show(self):
        """
        'doc'
        "html"
        "__all__"
        "__None__"
        """
        show_output(self.document, open_type=self.open_type, output_doc=self.output_path)


if __name__ == '__main__':
    input_doc = 'input.docx'
    output_doc = 'output_doc.docx'

    # --- 找出table_i中特定的标签, 如 {{ name }}, 然后将其替换为content_dc中的对应内容
    context_dc = {
        # 'portrailPicture': 'asdf',
        'signaturePicture': 'temp/a1.jpg',       # 手写电子签名
        'name': '博得',
        'phone': 18510779999,
        'sex': '男',
        'id_card': 43250311111111,
        'card_type': '大陆身份证',
        'work_unit': '新东方烹饪学校',
        'education_degree': '小学生',
        'school': '蓝翔',
        'month': 2,
        'year': 2021,
    }

    op = OperateDoc(input_path=input_doc, output_path=output_doc, context_dc=context_dc)

    op.traverse()
    op.show()
    1
